// Binary Clock .NET (systray clock displays time in binary notation)
// Copyright (C) 2004  Bartosz 'Xebord' Janowski
//					   (email: xebord at siodemka p lodz pl)
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.



using System;
using System.Threading;
using System.Windows.Forms;
using System.Timers;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Resources;
	
[assembly: AssemblyVersionAttribute("1.0")]
[assembly: AssemblyDescriptionAttribute("The clock in the tray displays time in binary notation.")]
[assembly: AssemblyCompanyAttribute("Author: Bartosz 'Xebord' Janowski")]
[assembly: AssemblyTitle("Binary Clock .NET")]
[assembly: AssemblyProductAttribute("Binary Clock .NET")]
[assembly: AssemblyCultureAttribute("")]

namespace BinClock
{
	/// <summary>
	/// 
	/// </summary>
	public class BinClock
	{
		private String				m_AppInfo;

		private NotifyIcon			m_NIcon;
		private int					m_Minute = -1;
		private Thread				m_SynchronizeThread;
		private int					m_Count = 0;
		private System.Timers.Timer	m_Timer;
		private ResourceManager		m_ResManager;
		private Bitmap 				m_OnDiod;
		private Bitmap 				m_OffDiod;

		/// <summary>
		/// BinClock ctor.
		/// </summary>
		public BinClock()
		{
			m_NIcon = new NotifyIcon();
			m_NIcon.Visible = true;
			m_NIcon.ContextMenu = new ContextMenu();
			m_NIcon.ContextMenu.MenuItems.Add( 0, new MenuItem( "About", new EventHandler( On_Click_About)));
			m_NIcon.ContextMenu.MenuItems.Add( 1, new MenuItem( "Quit", new EventHandler( On_Click_Quit)));

			m_Timer = new System.Timers.Timer();
			m_Timer.Elapsed +=new ElapsedEventHandler( m_Timer_Tick);
			m_Timer.Interval = 10000;

			m_SynchronizeThread = new Thread( new ThreadStart( SynchronizeTimer));
			m_SynchronizeThread.Start();
			
			m_ResManager = new ResourceManager("Diods", Assembly.GetExecutingAssembly());
				
			m_OnDiod = (Bitmap) m_ResManager.GetObject( "on2");
			m_OffDiod = (Bitmap) m_ResManager.GetObject( "off2");

			LoadAboutInfo();

			BuildClock();
		}
		/// <summary>
		/// Build and show binary clock in a tray.
		/// </summary>
		protected void BuildClock()
		{
			System.Console.Error.WriteLine( DateTime.Now.TimeOfDay.ToString());

			if( DateTime.Now.Minute != m_Minute)
			{
				try
				{
					Bitmap bitmap = new Bitmap( 32, 32, PixelFormat.Format24bppRgb);
					Graphics gr = Graphics.FromImage( bitmap);

					gr.TranslateTransform( 0, 24);

					DrawDigit( gr, Int2Bin( DateTime.Now.Hour / 10), 0); // godz, dzies
					DrawDigit( gr, Int2Bin( DateTime.Now.Hour % 10), 8); // godz, jednosc

					DrawDigit( gr, Int2Bin( DateTime.Now.Minute / 10), 16); // min, dzies
					DrawDigit( gr, Int2Bin( DateTime.Now.Minute % 10), 24); // min, jedn

					Icon i = FlimFlan.IconEncoder.Converter.BitmapToIcon( bitmap);

					m_Count++;

					m_NIcon.Icon = i;

					m_NIcon.Text = GetDate();

					gr.Dispose();
					bitmap.Dispose();

					m_Minute = DateTime.Now.Minute;

					System.Console.Error.WriteLine( "New minute!");
				}

				catch( Exception ex)
				{
					System.Console.Error.WriteLine( ex.ToString());
				}
			}
		}

		[DllImport("msvcr71.dll", CallingConvention = CallingConvention.Cdecl)]
		public static extern int _controlfp(int n, int mask);

		/// <summary>
		/// 'About' item menu event handler. Choose this item to read about this application.
		/// </summary>
		/// <param name="sender">The source of the event. </param>
		/// <param name="e">Contains the event data.</param>
		protected void On_Click_About( object sender, EventArgs e)
		{
			// this magic commands are against error in .NET 1.1
			/*
			const int _RC_NEAR = 0x00000000;
			const int _PC_53 = 0x00010000;
			const int _EM_ZERODIVIDE = 0x00000008;
			const int _EM_OVERFLOW = 0x00000004;
			const int _EM_UNDERFLOW = 0x00000002;
			const int _EM_DENORMAL = 0x00080000;
			const int _CW_DEFAULT = ( _RC_NEAR + _PC_53 + _EM_INVALID +
					  _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT +
					  _EM_DENORMAL);
			*/
			const int _EM_INEXACT = 0x00000001;
			const int _EM_INVALID = 0x00000010;
			
			const int _CW_DEFAULT = ( _EM_INVALID + _EM_INEXACT);
			
			_controlfp(_CW_DEFAULT, 0xfffff);



			AboutForm aboutform = new AboutForm( m_NIcon.Icon, m_AppInfo);

			aboutform.ShowDialog();
			aboutform.Dispose();
		}

		/// <summary>
		/// 'Quit' event handler. Choose this item to quit application.
		/// </summary>
		/// <param name="sender">The source of the event. </param>
		/// <param name="e">Contains the event data.</param>
		protected void On_Click_Quit( object sender, EventArgs e)
		{
			if( m_SynchronizeThread.IsAlive)
				m_SynchronizeThread.Abort();

			m_OnDiod.Dispose();
			m_OffDiod.Dispose();
			m_NIcon.Dispose();
			Application.Exit();
		}

		private void m_Timer_Tick(object source, ElapsedEventArgs e)
		{
			BuildClock();
		}

		/// <summary>
		/// Get actual time and date.
		/// </summary>
		/// <returns>Return time/date string. </returns>
		protected string GetDate()
		{
			return( DateTime.Now.ToShortTimeString() + "\n"	+ DateTime.Now.Day.ToString() 
				+ "-" + DateTime.Now.Month.ToString() + "-" + DateTime.Now.Year.ToString());
		}
		
		protected void LoadAboutInfo()
		{
			Assembly assmbl = Assembly.GetAssembly( this.GetType());
			object[] attrs = assmbl.GetCustomAttributes( typeof(AssemblyCompanyAttribute), true);

			AssemblyCompanyAttribute company = (AssemblyCompanyAttribute) attrs[0];
			m_AppInfo = company.Company.ToString();

			m_AppInfo += "\nVersion: ";
			m_AppInfo += assmbl.GetName().Version.ToString();
		}

		protected void SynchronizeTimer()
		{
			System.Console.Error.WriteLine( "Start Thead");
			int wait = ( 61 - DateTime.Now.Second) * 1000;
			System.Console.Error.WriteLine( "Wait: " + wait);
			Thread.Sleep( wait);

			BuildClock();

			System.Console.Error.WriteLine( "Start Timer");

			m_Timer.Start();
			System.Console.Error.WriteLine( m_Timer.ToString());
		}
		
		private void DrawDigit( Graphics g, String digit, int x)
		{
			int y = 0;
			try
			{
				for( int a = 0; a < digit.Length; a++)
				{
					if( digit[a] == '1')
						g.DrawImage( m_OnDiod, x, y - ( a * 8));
					else
						g.DrawImage( m_OffDiod, x, y - ( a * 8));
				}
			}
			catch( Exception e)
			{
				System.Console.Error.WriteLine( e.ToString());			
			}
		}

		/// <summary>
		/// Convert decimal digit to binary.
		/// </summary>
		/// <param name="num">Decimal digit.</param>
		/// <returns>Binary digit in string.</returns>
		private String Int2Bin( int num)
		{
			String str = String.Empty ;

			for( int a = 0; a < 4; a ++)
				str = String.Concat( str, ( num & (int)(Math.Pow( 2, a))) > 0 ? "1" : "0");
			return str;
		}


		/// <summary>
		/// Main function of application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			bool alone;
			Mutex mutex = new Mutex( true, "Xebord.BinClock", out alone);

			if( alone)
			{
				BinClock clock = new BinClock();

				Application.Run();
				GC.KeepAlive( mutex);
			}
			else
			{
				MessageBox.Show( "Just one 'Binary Clock' instance!", "Warning: Binary Clock", 
					MessageBoxButtons.OK, MessageBoxIcon.Warning );
			}	
		}
	}
}
